{% load static %}
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>消息中心</title>

<!-- ≡ 第三方库 -->
<link rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<style>
  /* ≡ 页面基础布局 / 颜色 */
      /* ===== 新增背景设置 ===== */
    body {
      font-family:"Segoe UI",sans-serif;
      margin:0;
      min-height:100vh;
      /* 背景图片设置 */
      background: url('{% static "images/background.jpg" %}') no-repeat center center fixed;
      background-size: cover;
      position: relative;
    }
    /* 确保内容在遮罩层上方 */
    .wrapper {
      position: relative;
      z-index: 1;
      max-width:960px;
      margin:40px auto;
      background:#fff;
      padding:24px 32px;
      border-radius:16px;
      box-shadow:0 6px 18px rgba(0,0,0,.12);
    }
  h1{margin:0;color:#2563eb;display:flex;align-items:center;font-size:26px}
  h1 i{margin-right:10px}

  /* ≡ 顶部标签 */
  .tabs{display:flex;gap:14px;margin:22px 0}
  .tab-btn{padding:6px 14px;border:1px solid #e5e7eb;border-radius:8px;
           background:#f9fafb;cursor:pointer;font-size:14px}
  .tab-btn.active{background:#2563eb;color:#fff;border-color:#2563eb}

  /* ≡ 表格 */
  table{width:100%;border-collapse:collapse;font-size:14px}
  th,td{padding:8px 6px;border-bottom:1px solid #f1f1f1;text-align:left}
  th{background:#f9fafb;font-weight:600;color:#374151}

  /* ≡ 底部导航 */
  .nav{margin-top:26px;display:flex;gap:12px;justify-content:center}
  .nav a{padding:8px 18px;border-radius:8px;text-decoration:none;
         color:#fff;font-size:14px}
  .nav .back{background:#2563eb}

  /* ≡ Chart 容器 */
  #chartBox{margin-top:28px}
  
  /* ≡ 警报弹窗 */
  .alert-container {
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: 12px;
  }
  
  .alert-box {
    background: #fff;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    padding: 16px;
    width: 300px;
    border-left: 4px solid #ff5252;
    display: flex;
    justify-content: space-between;
    animation: slideIn 0.5s ease-out;
    transform: translateX(0);
  }
  
  .alert-box.warning { border-color: #2563eb; }
  .alert-box.danger  { border-color: #ff5252; }
  
  .alert-content {
    flex: 1;
  }
  
  .alert-title {
    font-weight: bold;
    margin-bottom: 6px;
    color: #333;
  }
  
  .alert-message {
    font-size: 14px;
    color: #666;
  }
  
  .alert-close {
    cursor: pointer;
    color: #999;
    align-self: flex-start;
  }
  
  @keyframes slideIn {
    from { transform: translateX(100%); opacity: 0; }
    to   { transform: translateX(0); opacity: 1; }
  }
</style>
</head>
<body>
<div class="wrapper">
  <h1><i class="fas fa-bell"></i>消息中心</h1>

  <!-- ≡ 顶部标签 + 全房采集按钮 -->
  <div class="tabs">
    <button class="tab-btn active" data-tab="readings">温湿度记录</button>
    <button class="tab-btn"        data-tab="logs">操作日志</button>
    <button id="btnCollect" class="tab-btn"
            style="margin-left:auto;background:#10b981;color:#fff">
      <i class="fas fa-sync-alt"></i> 全房采集
    </button>
  </div>

  <!-- ≡ 数据表、折线图容器 -->
  <div id="tableBox">加载中…</div>
  <div id="chartBox" style="display:none">
    <canvas id="lineChart" height="320"></canvas>
  </div>

  <!-- ≡ 底部返回 -->
  <div class="nav">
    <a href="/index/" class="back"><i class="fas fa-home"></i> 返回首页</a>
  </div>
  
  <!-- ≡ 警报弹窗容器 -->
  <div class="alert-container" id="alertContainer"></div>
</div>

<script>
/* ============================================================
 * 1. 全局常量 / 状态
 * ========================================================== */
const PREFIX = '/monitor';          // 监控模块 URL 前缀
let rooms = [];                     // 房间列表 [{room_id,name}]
let readings = [];                  // 温湿度记录
let logs = [];                      // 操作日志
let chart   = null;                 // Chart.js 实例
let alertInterval = null;           // 警报检查定时器

/* ------------------------------------------------------------
 * 2. 通用 fetch 封装
 * ---------------------------------------------------------- */
async function api(url, opt = {}) {
  const res = await fetch(url, {
    ...opt,
    headers: {'Accept':'application/json','Content-Type':'application/json'}
  });
  const t = await res.text(); let d = {};
  try{ d = t ? JSON.parse(t) : {}; }catch{}
  if(!res.ok) throw Error(d.detail || d.message || res.statusText);
  return d;
}

/* ------------------------------------------------------------
 * 3. 全房采集：一次性为所有房间写入新读数
 * ---------------------------------------------------------- */
const btnCollect = document.getElementById('btnCollect');
btnCollect.onclick = async () => {
  btnCollect.disabled = true;
  btnCollect.textContent = '采集中…';
  try {
    rooms = await api('/rooms/');                              // 1) 拉房间
    // 2) 并行逐房写入
    await Promise.all(
      rooms.map(r => api(`${PREFIX}/sensor_readings/collect/?room_id=${r.room_id}`,
                         {method:'POST'}))
    );
    alert('全部房间采集完成');
    await loadData();                                          // 3) 重新拉数据并刷新
  } catch(e){
    alert('采集失败：' + e.message);
  }
  btnCollect.disabled = false;
  btnCollect.innerHTML = '<i class="fas fa-sync-alt"></i> 全房采集';
};

/* ------------------------------------------------------------
 * 4. 标签切换（tab 组件）
 * ---------------------------------------------------------- */
document.querySelector('.tabs').addEventListener('click', e=>{
  const btn = e.target.closest('.tab-btn'); if(!btn) return;
  document.querySelectorAll('.tab-btn').forEach(b=>b.classList.toggle('active', b===btn));
  const tab = btn.dataset.tab || 'readings';
  btnCollect.style.display = tab==='readings' ? 'inline-block' : 'none';
  render(tab);
});

/* ------------------------------------------------------------
 * 5. 首次加载数据 - 修复版本
 * ---------------------------------------------------------- */
async function loadData(){
  try {
    // 加载消息数据
    const msgs = await api(`${PREFIX}/messages/api/`);
    readings   = msgs.filter(m => m.message_type === 'reading');
    logs       = msgs.filter(m => m.message_type === 'log');
    
    // 加载房间数据
    if(!rooms.length) rooms = await api('/rooms/');
    
    // 渲染当前标签页
    render(document.querySelector('.tab-btn.active')?.dataset.tab || 'readings');
    
    // 启动警报检查定时器（每10秒检查一次）
    if (!alertInterval) {
      alertInterval = setInterval(checkForAlerts, 10000);
      // 页面加载时立即检查一次
      setTimeout(checkForAlerts, 1000);
    }
  } catch(e){
    document.getElementById('tableBox').textContent = '加载失败：' + e.message;
    console.error('数据加载错误:', e);
  }
}

// 页面加载完成后调用
window.addEventListener('DOMContentLoaded', () => {
  loadData();
});

/* ------------------------------------------------------------
 * 6. 主渲染函数：根据 tab 更新表格 / 图表 - 修复版本
 * ---------------------------------------------------------- */
function render(tab){
  const box      = document.getElementById('tableBox');
  const chartBox = document.getElementById('chartBox');

  if(tab === 'readings'){
    chartBox.style.display = 'block';
    
    try {
      drawChart(); // 确保在渲染表格前绘制图表
    } catch(e) {
      console.error('绘制图表时出错:', e);
      box.textContent = '绘制图表时出错，请检查控制台';
      return;
    }
    
    box.innerHTML = readings.length ? `
      <table>
        <thead>
          <tr><th>批次</th><th>房间</th><th>温度(℃)</th><th>湿度(%)</th></tr>
        </thead>
        <tbody>
          ${readings.map(r => `
            <tr>
              <td>${batchKeyOf(r)}</td>
              <td>${r.room_name}</td>
              <td>${r.temperature}</td>
              <td>${r.humidity}</td>
            </tr>`).join('')}
        </tbody>
      </table>` : '暂无记录';
  }else{
    chartBox.style.display = 'none';
    if(chart) chart.destroy(); // 确保图表被销毁
    chart = null;
    
    box.innerHTML = logs.length ? `
      <table>
        <thead>
          <tr><th>时间</th><th>用户</th><th>房间</th><th>设备</th>
              <th>动作</th><th>旧值</th><th>新值</th></tr>
        </thead>
        <tbody>
          ${logs.map(l => `
            <tr>
              <td>${l.created_at.slice(0,19).replace('T',' ')}</td>
              <td>${l.user_name   || '—'}</td>
              <td>${l.room_name   || '—'}</td>
              <td>${l.device_name || '—'}</td>
              <td>${l.action_type}</td>
              <td>${l.old_value   || '—'}</td>
              <td>${l.new_value   || '—'}</td>
            </tr>`).join('')}
        </tbody>
      </table>` : '暂无记录';
  }
}

/* ------------------------------------------------------------
 * 7. 折线图绘制（Chart.js） - 修复版本
 *    确保正确处理数据缺失和图表实例
 * ---------------------------------------------------------- */
function batchKeyOf(rec){
  /* 只取到「秒」级别，抹掉毫秒差异，转成字符串做 key */
  return rec.created_at.slice(0,19);        // e.g. 2025-06-17T09:30:11
}

function drawChart(){
  /* --- 1. 所有批次作为 X 轴（自然排序） --- */
  const labels = [...new Set(readings.map(batchKeyOf))].sort();

  /* --- 2. 以 room_id 分组数据 --- */
  const grouped = {};
  readings.forEach(r=>{
    (grouped[r.room_id] = grouped[r.room_id] || []).push(r);
  });

  /* --- 3. 构造数据集（温度实线 / 湿度虚线） --- */
  const ds = [];
  const colors = ['#ef4444','#3b82f6','#10b981','#f59e0b',
                  '#8b5cf6','#ec4899','#14b8a6'];
  let idx = 0;

  for(const [roomId,list] of Object.entries(grouped)){
    const name = list[0].room_name;
    const col  = colors[idx++ % colors.length];

    ds.push({
      label:`${name} 温度`,
      spanGaps:true,                         // ★ 连线：忽略缺口
      borderColor:col,
      backgroundColor:col,
      tension:.3,
      data: labels.map(l=>{
        const rec=list.find(x=>batchKeyOf(x)===l);
        return rec ? Number(rec.temperature) : null;
      }),
      yAxisID:'yTemp'
    });

    ds.push({
      label:`${name} 湿度`,
      spanGaps:true,
      borderColor:col,
      backgroundColor:col,
      borderDash:[6,4],                      // 虚线
      tension:.3,
      data: labels.map(l=>{
        const rec=list.find(x=>batchKeyOf(x)===l);
        return rec ? Number(rec.humidity) : null;
      }),
      yAxisID:'yHum'
    });
  }

  /* --- 4. 初始化或更新 Chart --- */
  const ctx = document.getElementById('lineChart').getContext('2d');
  
  // 如果图表已存在，先销毁再重建
  if(chart){
    chart.destroy();
  }
  
  // 确保有数据可显示
  if(ds.length === 0) {
    document.getElementById('chartBox').textContent = '没有足够数据绘制图表';
    return;
  }

  chart = new Chart(ctx,{
    type:'line',
    data:{labels,datasets:ds},
    options:{
      interaction:{mode:'nearest',intersect:false},
      scales:{
        yTemp:{type:'linear',position:'left',
               title:{display:true,text:'温度(℃)'}},
        yHum :{type:'linear',position:'right',min:0,max:100,
               grid:{drawOnChartArea:false},
               title:{display:true,text:'湿度(%)'}}
      },
      plugins:{legend:{position:'bottom'}}
    }
  });
}

/* ------------------------------------------------------------
 * 8. 警报系统：检查温湿度是否超过阈值
 * ---------------------------------------------------------- */
// 已显示过的警报ID缓存（防止重复显示）
const activeAlerts = {};

async function checkForAlerts() {
  try {
    // 获取最新的读数
    const latestReadings = await api(`${PREFIX}/sensor_readings/latest/`);
    
    // 检查每个读数是否超过阈值
    latestReadings.forEach(reading => {
      // 检查是否超过阈值
      const isTempOver = reading.temperature > 48;
      const isHumOver = reading.humidity > 85;
      
      if (isTempOver || isHumOver) {
        // 获取房间名称
        const room = rooms.find(r => r.room_id === reading.room_id);
        const roomName = room ? room.name : `房间 ${reading.room_id}`;
        
        // 显示或更新警报
        showAlert(reading, roomName, isTempOver, isHumOver);
      } else {
        // 如果数据正常，移除警报（如果存在）
        removeAlert(reading.room_id);
      }
    });
    
  } catch (e) {
    console.error('检查警报时出错:', e);
  }
}

function showAlert(reading, roomName, isTempOver, isHumOver) {
  const container = document.getElementById('alertContainer');
  const roomId = reading.room_id;
  
  // 检查是否已有该房间的警报
  if (activeAlerts[roomId]) {
    // 更新现有警报内容
    const alertBox = activeAlerts[roomId];
    const messageElement = alertBox.querySelector('.alert-message');
    
    // 生成警报消息
    let messageHTML = '';
    if (isTempOver && isHumOver) {
      messageHTML = `
        房间 <strong>${roomName}</strong> 环境异常：
        <div style="margin-top:8px">
          <div>温度过高，可能有火情: <strong>${reading.temperature}℃</strong></div>
          <div>湿度过高，可能有漏水情况: <strong>${reading.humidity}%</strong></div>
        </div>
      `;
    } else if (isTempOver) {
      messageHTML = `房间 <strong>${roomName}</strong> 温度过高: <strong>${reading.temperature}℃</strong>`;
    } else if (isHumOver) {
      messageHTML = `房间 <strong>${roomName}</strong> 湿度过高: <strong>${reading.humidity}%</strong>`;
    }
    
    // 更新警报内容
    messageElement.innerHTML = messageHTML;
    
    // 更新严重程度
    alertBox.classList.remove('warning', 'danger');
    if (reading.temperature > 50 || reading.humidity > 90) {
      alertBox.classList.add('danger');
    } else {
      alertBox.classList.add('warning');
    }
  } else {
    // 创建新的警报元素
    const alertBox = document.createElement('div');
    alertBox.className = 'alert-box';
    
    // 生成警报消息
    let messageHTML = '';
    if (isTempOver && isHumOver) {
      messageHTML = `
        房间 <strong>${roomName}</strong> 环境异常：
        <div style="margin-top:8px">
          <div>温度过高: <strong>${reading.temperature}℃</strong></div>
          <div>湿度过高: <strong>${reading.humidity}%</strong></div>
        </div>
      `;
    } else if (isTempOver) {
      messageHTML = `房间 <strong>${roomName}</strong> 温度过高: <strong>${reading.temperature}℃</strong>`;
    } else if (isHumOver) {
      messageHTML = `房间 <strong>${roomName}</strong> 湿度过高: <strong>${reading.humidity}%</strong>`;
    }
    
    // 设置警报内容
    alertBox.innerHTML = `
      <div class="alert-content">
        <div class="alert-title">环境警报！</div>
        <div class="alert-message">
          ${messageHTML}
        </div>
      </div>
      <div class="alert-close">&times;</div>
    `;
    
    // 添加关闭按钮事件
    alertBox.querySelector('.alert-close').addEventListener('click', () => {
      removeAlert(roomId);
    });
    
    // 添加到容器
    container.appendChild(alertBox);
    
    // 存储警报引用
    activeAlerts[roomId] = alertBox;
    
    // 设置严重程度
    if (reading.temperature > 50 || reading.humidity > 90) {
      alertBox.classList.add('danger');
    } else {
      alertBox.classList.add('warning');
    }
  }
}

function removeAlert(roomId) {
  if (activeAlerts[roomId]) {
    activeAlerts[roomId].remove();
    delete activeAlerts[roomId];
  }
}

// 页面卸载时清除定时器
window.addEventListener('beforeunload', () => {
  if (alertInterval) {
    clearInterval(alertInterval);
  }
  
  // 清理图表实例
  if(chart) {
    chart.destroy();
  }
});
</script>
</body>
</html>